package com.xwaf.platform.web.config.shiro;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 
 * <p>
 * Shiro配置<br>
 * 1.LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类<br>
 * 负责org. apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。<br>
 * 主要是AuthorizingRealm类的子类， 以及EhCacheManager类。<br>
 * <br>
 * 2.HashedCredentialsMatcher，这个类是为了对密码进行编码的，防止密码在数据库里明码保存<br>
 * 当然在登陆认证的生活， 这个类也负责对form里输入的密码进行编码。<br>
 * <br>
 * 3.ShiroRealm，这是个自定义的认证类，继承自AuthorizingRealm，负责用户的认证和权限的处理，可以参考JdbcRealm的实现。
 * <br>
 * 4.EhCacheManager，缓存管理，用户登陆成功后，把用户信息和权限信息缓存起来，然后每次用户请求时，放入用户的session中 <br>
 * 如果不设置这个bean，每个请求都会查询一次数据库。 <br>
 * <br>
 * 5.SecurityManager，权限管理，这个类组合了登陆，登出，权限，session的处理，是个比较重要的类。<br>
 * <br>
 * 6.ShiroFilterFactoryBean，是个factorybean，为了生成ShiroFilter。<br>
 * 它主要保持了三项数据， securityManager，filters，filterChainDefinitionManager。<br>
 * <br>
 * 7.DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。<br>
 * <br>
 * 8.AuthorizationAttributeSourceAdvisor，shiro里实现的Advisor类<br>
 * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。<br>
 * <p>
 * 
 * @author 李聪 <br>
 * @email xwaf_love@yeah.net <br>
 * @since JDK 1.8<br>
 * @date 2019年12月30日 上午9:18:37 <br>
 * @see 无<br>
 *      Copyright (c) 2019, xwaf_love@yeah.net All Rights Reserved.<br>
 */
@Configuration
public class ShiroConfig {

	@Bean("securityManager")
	public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(oAuth2Realm);
		securityManager.setRememberMeManager(null);
		return securityManager;
	}

	/**
	 * 配置过滤器工厂，设置对应的过滤条件和跳转条件<br>
	 * ShiroFilterFactoryBean，是个factorybean，为了生成ShiroFilter。<br>
	 * 它主要保持了三项数据，securityManager，filters，filterChainDefinitionManager<br>
	 * 
	 * @param securityManager<br>
	 * @return
	 */
	@Bean("shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
		shiroFilter.setSecurityManager(securityManager);
		// 登录，无权限是跳转的路径
		// shiroFilter.setLoginUrl("/login");
		// 登录成功后跳转的路径
		// shiroFilter.setSuccessUrl("/index");
		// 错误页面，认证不通过跳转
		// shiroFilter.setUnauthorizedUrl("/error");
		// oauth过滤
		Map<String, Filter> filters = new HashMap<>();
		filters.put("oauth2", new OAuth2Filter());
		shiroFilter.setFilters(filters);
		/**
		 * Shiro 内置过滤器，过滤链定义<br>
		 * 从上向下顺序执行 常用的过滤器： anon:无需认证（登录）<br>
		 * 可以访问 (它对应的过滤器里面是空的，什么都没做，可以理解为不拦截)<br>
		 * <br>
		 * 
		 * @see org.apache.shiro.web.filter.authc.AnonymousFilter<br>
		 *      authc:必须认证才可以访问(该过滤器下的页面必须验证后才能访问，它是Shiro内置的一个拦截器)<br>
		 *      所有url都必须认证通过才可以访问；anon：所有url都都可以匿名访问<br>
		 * @see org.apache.shiro.web.filter.authc.FormAuthenticationFilter<br>
		 *      user:只要登录过，并且记住了密码，如果设置了rememberMe的功能可以直接访问<br>
		 *      perms:该资源必须得到资源权限才可以访问 role:该资源必须得到角色的权限才可以访问<br>
		 */
		Map<String, String> filterMap = new LinkedHashMap<>();
		filterMap.put("/webjars/**", "anon");// anon表示可以匿名访问
		filterMap.put("/druid/**", "anon");
		filterMap.put("/app/**", "anon");
		filterMap.put("/auth/login", "anon");// 登陆
		filterMap.put("/x/**", "anon");// 测试的(公共请求)
		filterMap.put("/check", "anon");// 健康检查
		filterMap.put("/swagger/**", "anon");
		filterMap.put("/v2/api-docs", "anon");
		filterMap.put("/", "anon");// 指向API
		filterMap.put("/swagger-ui.html", "anon");// 跳转至指向API
		filterMap.put("/swagger-resources/**", "anon");

		// 静态资源
		filterMap.put("/static/**", "anon");

		filterMap.put("/**", "oauth2");
		shiroFilter.setFilterChainDefinitionMap(filterMap);
		return shiroFilter;
	}

	/**
	 *
	 * <p>
	 * Shiro生命周期处理器<br>
	 * 此方法需要用static作为修饰词，否则无法通过@Value()注解的方式获取配置文件的值<br>
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:22:15 <br>
	 * @see 无<br>
	 * @return
	 * @demo <br>
	 */
	@Bean("lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}
}
